package front

import (
	"encoding/json"
	"errors"
	"fmt"
	"geekai/api/dto/common"
	"geekai/api/dto/response"
	"geekai/api/handler"
	"geekai/core"
	"geekai/core/store"
	"geekai/core/types"
	"geekai/model"
	"geekai/service"
	"geekai/service/dalle"
	"geekai/service/oss"
	"geekai/utils"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"github.com/golang-jwt/jwt/v5"
	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

type FunctionHandler struct {
	handler.BaseHandler
	uploadManager *oss.UploaderManager
	dallService   *dalle.Service
	userService   *service.UserService
	redisHash     *store.RedisHash
}

func NewFunctionHandler(app *core.AppServer, db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, dallService *dalle.Service, userService *service.UserService) *FunctionHandler {
	return &FunctionHandler{
		BaseHandler: handler.BaseHandler{
			App: app,
			DB:  db,
		},
		uploadManager: manager,
		dallService:   dallService,
		userService:   userService,
		redisHash:     store.NewRedisHash(types.HOT_SEARCH_KEY, redisCli),
	}
}

func (h *FunctionHandler) checkAuth(c *gin.Context) error {
	tokenString := c.GetHeader(types.UserAuthHeader)
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return []byte(h.App.Config.Session.SecretKey), nil
	})

	if err != nil {
		return fmt.Errorf("解析身份验证令牌出错：%v", err)
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok || !token.Valid {
		return errors.New("token 校验失败")
	}

	expr := utils.IntValue(utils.InterfaceToString(claims["expired"]), 0)
	if expr > 0 && int64(expr) < time.Now().Unix() {
		return errors.New("token 过期")
	}

	return nil
}

// WeiBo godoc
// @Summary      微博热搜
// @Description  微博热搜
// @Tags         前台-函数工具
// @Accept       json
// @Produce      json
// @Success      200  {object}  types.BizVo{data=string}
// @Router       /api/function/weibo [post]
// @Security ApiKeyAuth
func (h *FunctionHandler) WeiBo(c *gin.Context) {
	if err := h.checkAuth(c); err != nil {
		utils.ERROR(c, "身份验证失败："+err.Error())
		return
	}
	// 获取热搜数据
	weiBoDataStr, err := h.redisHash.HGet(types.WEIBO_HOT_SEARCH_FIELD)
	if err != nil {
		utils.ERROR(c, "获取微博热搜数据出现错误："+err.Error())
		return
	}

	var formattedSlice []response.WeiBoFormattedTopic
	err = json.Unmarshal([]byte(weiBoDataStr), &formattedSlice)
	if err != nil {
		utils.ERROR(c, "获取到的微博热搜数据不符合格式："+err.Error())
		return
	}
	// 获取爬虫更新时间
	updateTimeStr, err := h.redisHash.HGet(types.HOT_SEARCH_UPDATE_TIME_FIELD)
	if err != nil {
		log.Errorf("获取爬虫更新时间出现错误：%v\n", err)
		updateTimeStr = strconv.FormatInt(time.Now().Unix(), 10)
	}
	builder := make([]string, 0)
	updatedTime, _ := strconv.ParseInt(updateTimeStr, 10, 64)
	builder = append(builder, fmt.Sprintf("**%s**，最新更新：%s", "新浪微博 ‧ 今日热搜", utils.StampMill2str(updatedTime)))
	for i, v := range formattedSlice {
		builder = append(builder, fmt.Sprintf("%d、 [%s](%s) ", i+1, v.Title, v.URL))
	}
	utils.SUCCESS(c, strings.Join(builder, "\n\n"))
}

// ZaoBao godoc
// @Summary      今日早报
// @Description  今日早报
// @Tags         前台-函数工具
// @Accept       json
// @Produce      json
// @Success      200  {object}  types.BizVo{data=string}
// @Router       /api/function/zaobao [post]
// @Security ApiKeyAuth
func (h *FunctionHandler) ZaoBao(c *gin.Context) {
	if err := h.checkAuth(c); err != nil {
		utils.ERROR(c, "身份验证失败："+err.Error())
		return
	}
	// 获取热搜数据
	touTiaoDataStr, err := h.redisHash.HGet(types.TOU_TIAO_HOT_SEARCH_FIELD)
	if err != nil {
		utils.ERROR(c, "获取今日头条热搜数据出现错误："+err.Error())
		return
	}

	var formattedSlice []response.TouTiaoFormattedTopic
	err = json.Unmarshal([]byte(touTiaoDataStr), &formattedSlice)
	if err != nil {
		utils.ERROR(c, "获取到的今日头条热搜数据不符合格式："+err.Error())
		return
	}
	// 获取爬虫更新时间
	updateTimeStr, err := h.redisHash.HGet(types.HOT_SEARCH_UPDATE_TIME_FIELD)
	if err != nil {
		log.Errorf("获取爬虫更新时间出现错误：%v\n", err)
		updateTimeStr = strconv.FormatInt(time.Now().Unix(), 10)
	}
	builder := make([]string, 0)
	updatedTime, _ := strconv.ParseInt(updateTimeStr, 10, 64)
	builder = append(builder, fmt.Sprintf("**%s 早报：**", utils.StampMill2str(updatedTime)))
	for _, v := range formattedSlice {
		builder = append(builder, fmt.Sprintf("[%s](%s) ", v.Title, v.URL))
	}
	utils.SUCCESS(c, strings.Join(builder, "\n\n"))
}

// Dall3 godoc
// @Summary      Dall3 AI 绘图
// @Description  Dall3 AI 绘图
// @Tags         前台-函数工具
// @Accept       json
// @Produce      json
// @Param        req   body     map[string]interface{}  true  "会话参数"
// @Success      200  {object}  types.BizVo{data=string}
// @Router       /api/function/dall3 [post]
// @Security ApiKeyAuth
func (h *FunctionHandler) Dall3(c *gin.Context) {
	if err := h.checkAuth(c); err != nil {
		utils.ERROR(c, "身份验证失败："+err.Error())
		return
	}

	var params map[string]interface{}
	if err := c.ShouldBindJSON(&params); err != nil {
		utils.ERROR(c, types.InvalidArgs)
		return
	}

	log.Debugf("绘画参数：%+v", params)
	// 查询用户信息
	var user model.User
	res := h.DB.Where("id = ?", params["user_id"]).First(&user)
	if res.Error != nil {
		utils.ERROR(c, "当前用户不存在："+res.Error.Error())
		return
	}

	// 判断用户算力是否足够
	if user.Power < h.App.SysConfig.DallPower {
		utils.ERROR(c, "创建 DALL-E 绘图任务失败，算力不足")
		return
	}

	// 创建绘图任务
	prompt := utils.InterfaceToString(params["prompt"])
	task := types.DallTask{
		UserId:           user.Id,
		Prompt:           prompt,
		ModelId:          0,
		ModelName:        "dall-e-3",
		TranslateModelId: h.App.SysConfig.TranslateModelId,
		N:                1,
		Quality:          "standard",
		Size:             "1024x1024",
		Style:            "vivid",
		Power:            h.App.SysConfig.DallPower,
	}

	job := model.DallJob{
		UserId:   user.Id,
		Prompt:   prompt,
		Power:    h.App.SysConfig.DallPower,
		TaskInfo: utils.JsonEncode(task),
	}
	res = h.DB.Create(&job)

	if res.Error != nil {
		utils.ERROR(c, "创建 DALL-E 绘图任务失败："+res.Error.Error())
		return
	}

	task.Id = job.Id
	// 执行绘图任务
	content, err := h.dallService.Image(task, true)
	if err != nil {
		utils.ERROR(c, "DALL 绘图任务执行失败："+err.Error())
		return
	}

	// 扣减算力
	err = h.userService.DecreasePower(int(user.Id), job.Power, model.PowerLog{
		Type:   types.PowerConsume,
		Model:  task.ModelName,
		Remark: fmt.Sprintf("绘画提示词：%s", utils.CutWords(job.Prompt, 10)),
	})
	if err != nil {
		utils.ERROR(c, "扣减算力失败："+err.Error())
		return
	}
	utils.SUCCESS(c, content)
}

// List godoc
// @Summary      获取所有的工具函数列表
// @Description  获取所有的工具函数列表
// @Tags         前台-函数工具
// @Accept       json
// @Produce      json
// @Success      200  {object}  types.BizVo{data=[]common.Function}
// @Router       /api/function/list [get]
// @Security ApiKeyAuth
func (h *FunctionHandler) List(c *gin.Context) {
	var items []model.Function
	err := h.DB.Where("enabled", true).Find(&items).Error
	if err != nil {
		utils.ERROR(c, "查询工具函数信息失败："+err.Error())
		return
	}

	tools := make([]common.Function, 0)
	for _, v := range items {
		var f common.Function
		err = utils.CopyObject(v, &f)
		if err != nil {
			log.Errorf("数据拷贝失败：%v", err)
			continue
		}
		f.Action = ""
		f.Token = ""
		tools = append(tools, f)
	}
	utils.SUCCESS(c, tools)
}
